using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Network.Delegate;
using Network.Tcp.Awaitable;
using Network.Tcp.Pooling;
using Network.Tcp.NetSession;
using Network.Tcp.TcpConfig;
using Network.UtilityHelper;

namespace Network.Tcp
{
    public class ServerAgent : NetEngineBased, IDisposable
    {
        Socket _listener;
        bool _isruning = true;
        ServerConfig _config;
        private SessionType _sessionType = SessionType.Full;
        internal ServerAgent(
            SessionType sessionType,
            ServerConfig cfg,
            NotifyEventHandler<CompleteNotify, Session> completetionNotify)
            : base(sessionType, cfg, completetionNotify)
        {
            _config = cfg;
            _sessionType = sessionType;
            if (cfg.AppKeepAlive &&
                sessionType == SessionType.Packet)
            {
                var keepAliveThread = new Thread(AppKeepAliveWorkThread);
                keepAliveThread.IsBackground = true;
                keepAliveThread.Start();
            }
        }

        public IPEndPoint LocalEndPoint
        {
            get { return (IPEndPoint)_listener.LocalEndPoint; }
        }

        private void AppKeepAliveWorkThread()
        {
            byte[] emptyHeart = new byte[] { 0, 0, 0, 0 };

            while (_isruning)
            {

                for (int i = 0; i < _sessions.Count; i++)
                {
                    var session = (PackSessionBased)_sessions[i];
                    if (session.State != ConnectionState.Connected)
                    {
                        _logger.WriteLog("server_heart_thread != Connected");
                        _sessions.RemoveAt(i); i--;
                        continue;
                    }

                    //if (session.State == TcpSocketConnectionState.None
                    //|| session.State == TcpSocketConnectionState.Connecting)
                    //    continue;

                    if ((int)(DateTime.Now - session.HeartTime).TotalSeconds > 20)
                    {
                        _logger.WriteLog("server_heart_thread_timeout --state：" + session.State.ToString() + " present_time：" + DateTime.Now.ToString() + " heart_time：" + session.HeartTime.ToString());
                        if (session.State == ConnectionState.Connected)
                        {
                            Console.WriteLine("server timeout--");
                            session.Close(true);
                            _sessions.RemoveAt(i); i--;
                        }
                    }
                }
                Thread.Sleep(5000);
            }
        }

        public void Listen(IPEndPoint ipendPoint)
        {
            _isruning = true;

            _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            SetSocketOptions();

            _listener.Bind(ipendPoint);

            _listener.Listen(_config.PendingConnectionBacklog);

            var awaiter = _awaiterPool.Take();
            NetHelper.AcceptAsync(_listener, awaiter, Accept);
        }

        private void Accept(Awaiter awaiter, SocketError socketError)
        {
            if (socketError == SocketError.Success)
            {
                var acceptedSocket = awaiter.Args.AcceptSocket;
                var session = _sessionPool.Take();
                session.Attach(acceptedSocket);
                _sessions.Add(session);
                _completetionNotify?.Invoke(CompleteNotify.OnConnected, session);

                session.StartProcess();
            }
            else _logger.WriteLog("server_accept-fail");

            if (!_isruning) return;

            awaiter.Args.AcceptSocket = null;
            NetHelper.AcceptAsync(_listener, awaiter, Accept);
        }
        private void SetSocketOptions()
        {
            _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, _cfg.ReuseAddress);
        }
        public override void Broadcast(byte[] data)
        {
            Broadcast(data, 0, data.Length);
        }
        public override void Broadcast(byte[] data, int offset, int length)
        {
            if (_sessionType == SessionType.Full)
            {
                foreach (FullSessionBased session in _sessions)
                    session.SendAsync(data, offset, length);
            }
            else
            {
                foreach (PackSessionBased session in _sessions)
                    session.SendAsync(data, offset, length);
            }
        }

        public override void ClearConnections(bool notify)
        {
            foreach (Session session in _sessions)
                session.Close(notify);

            _sessions.Clear();
        }
        public override void Dispose()
        {
            try
            {
                _listener.Close(0);
                _listener = null;
            }
            catch (Exception e)
            {
                _logger.WriteLog("server_shutdown exception info：" + e.Message);
            }

            _logger.Dispose();
            _isruning = false;
            ClearConnections(false);
            _awaiterPool.Dispose();
            _sessionPool.Dispose();
        }
    }
}
